![GCS to S3を極力マネージドサービスで実現してみる](https://devio2023-media.developers.io/wp-content/uploads/2022/09/googlecloud-cloud-run.png)
GCS to S3を極力マネージドサービスで実現してみる
はじめに
データアナリティクス事業本部のkobayashiです。
Amazon S3からGoogle Cloud Storage(GCS)へオブジェクトを転送するサービスとしてはStorage Transfer Serviceを使うことでOne-timeの転送やスケジューリングで定期転送を行うことができます。 一方、GCSからS3への転送となるとフルマネージドなサービスがなかなかありません。今回極力マネージドなサービスを使ってGCSからS3へ転送する仕組みを作ってみたのでまとめます。
Storage Transfer Service | Google Cloud
Cloud Run JobsのシェルジョブでGCSからS3へ転送する
GCSからS3への転送を極力マネージドな仕組みで実現するために今回作ってみるのは以下のようなものです。
gsutil
のrsync
を使ってGCSからS3への転送を行う- Cloud Run Jobsのシェルジョブで
gsutil
を実行する.boto
にはシークレット情報を保存しない- シークレット(AWS接続情報)はSecret Managerで管理する
- 環境変数で転送元、転送先、シークレットを与える
それでは早速GCSからS3へ転送を行うCloud Run Jobsを作ってみます。Cloud Run Jobsの作成方法は公式ドキュメントの「 クイックスタート: Cloud Run でシェルジョブをビルドして作成する | Cloud Run のドキュメント | Google Cloud 」に詳しい手順が書かれていますのでこれを参考に作成してみます。
ジョブで実行するシェルスクリプトの作成
Cloud Run Jobsのジョブではgsutil rsync
コマンドを使ってでGCSからS3へ転送を行います。今回使うgsutilコマンドは以下のような使い方をします。
gsutil -m rsync -rdC gs://${SRC_PATH} s3://${DIST_PATH}
このコマンドを使ってGCSからS3への転送を行いますので、Cloud Run Jobsで実行するシェルスクリプトファイルは以下になります。
#!/bin/bash export BOTO_CONFIG=.boto echo ${SRC_PATH} # 転送元 echo ${DIST_PATH} # 転送先 echo "Credentials:aws_access_key_id=${AWS_ACCESS_KEY_ID}" # AWSアクセスキー gsutil -m -o "Credentials:aws_access_key_id=${AWS_ACCESS_KEY_ID}" -o "Credentials:aws_secret_access_key=${AWS_SECRET_ACCESS_KEY}" rsync -rdC gs://${SRC_PATH} s3://${DIST_PATH}
Cloud Run Jobsで実行するためGCSへのアクセスについては作成するジョブで使うサービスアカウントに権限を与えれば良いですが、S3へのアクセス情報となるアクセスキー・シークレットキーは通常.boto
の[Credentials]
ブロックに記述します。今回は.boto
に記述してしまうとビルドしたイメージ中にシークレットが含まれてしまうので、Secret Managerにそれらの情報は保存してジョブ実行時に環境変数として与えるようにします。
したがって合わせて使う.boto
は以下になります。
[s3] use-signv4=True host=s3.ap-northeast-1.amazonaws.com
gsutil
で使っている上記以外のオプション等細かい設定は以下の公式ドキュメントを参考してにしてください。
- グローバル コマンドライン オプション | Cloud Storage | Google Cloud
- rsync - Synchronize content of two buckets/directories | Cloud Storage | Google Cloud
ジョブ用のイメージ作成
上記のファイルを使いCloud Run Jobsで使うイメージをCloud Buildで作成します。以下のようなディレクトリ構成でファイルを配置します。
└── jobs ├── .boto ├── Dockerfile └── script.sh
.boto
とscript.sh
は前述したものを使います。
Dockerfile
は以下になります。
FROM google/cloud-sdk:alpine RUN apk update && \ apk upgrade && \ apk add --no-cache bash WORKDIR /workspace COPY .boto . COPY script.sh . RUN chmod +x ./script.sh CMD [ "./script.sh" ]
gsutil
を使いたいのでGoogle Cloud SDKがインストールされているgoogle/cloud-sdk
イメージをベースイメージとして使います。
それではCloud Buildでコンテナイメージを作成します。
gcloud builds submit -t "gcr.io/{プロジェクトID}/gcs-to-s3-job"
これでCloud Run Jobsでジョブとして使うイメージが作成できました。
Secret Managerとサービスアカウントの作成
次にCloud Run Jobsで使うSecret Managerの作成と実行時に使うサービスアカウントの作成を行います。
はじめにSecret ManagerにAWS認証情報のアクセスキーとシークレットキー以下のコマンドで作成します。
gcloud secrets create aws_access_key_id --data-file=/tmp/aws_access_key_id gcloud secrets create aws_secret_access_key --data-file=/tmp/aws_secret_access_key
次にサービスアカウントを作ります。サービスアカウトに与える権限はSecret Managerの読み取り権限とGCSの管理者権限を与えます。
gcloud iam service-accounts create cloud-run-sa gcloud projects add-iam-policy-binding {プロジェクトID} \ --member serviceAccount:cloud-run-sa@{プロジェクトID}.iam.gserviceaccount.com\ --role="roles/storage.admin" gcloud projects add-iam-policy-binding {プロジェクトID} \ --member serviceAccount:cloud-run-sa@{プロジェクトID}.iam.gserviceaccount.com\ --role "roles/secretmanager.secretAccessor"
Cloud Run Jobsのジョブを作成
イメージを作成しSecret Managerとサービスアカウントを作成したのでこれらを使ってCloud Run Jobsのジョブを以下のコマンドで作成します。
gcloud beta run jobs create gcs-to-s3-job \ --image gcr.io/{プロジェクトID}/gcs-to-s3-job \ --set-env-vars SRC_PATH={転送元バケット}/s3_sample,DIST_PATH={転送先バケット}/s3_sample \ --set-secrets AWS_ACCESS_KEY_ID=aws_access_key_id:latest,AWS_SECRET_ACCESS_KEY=aws_secret_access_key:latest \ --max-retries 3 \ --region asia-northeast1 \ --project {プロジェクトID} \ --service-account cloud-run-sa@{プロジェクトID}.iam.gserviceaccount.com
これでジョブが作成できたので以下のコマンドでCloud Run Jobsを実行します。
gcloud beta run jobs execute gcs-to-s3-job --region asia-northeast1
するとジョブが実行されGCSで転送元として指定したパス配下のオブジェクトがS3の転送先として指定したパスに作成されます。
$ gsutil ls -l gs://{転送元バケット}/s3_sample/ 0 2023-05-20T01:25:29Z gs://{転送元バケット}/s3_sample/ 1045260 2023-05-20T01:25:30Z gs://{転送元バケット}/s3_sample/streaming_test1.csv 1045260 2023-05-20T01:25:30Z gs://{転送元バケット}/s3_sample/streaming_test2.csv 1045260 2023-05-20T01:25:30Z gs://{転送元バケット}/s3_sample/streaming_test3.csv 1045260 2023-05-20T01:25:30Z gs://{転送元バケット}/s3_sample/streaming_test4.csv 1045260 2023-05-20T01:25:30Z gs://{転送元バケット}/s3_sample/streaming_test5.csv 1045260 2023-05-20T01:25:30Z gs://{転送元バケット}/s3_sample/streaming_test6.csv 1045260 2023-05-20T01:25:30Z gs://{転送元バケット}/s3_sample/streaming_test7.csv 1045260 2023-05-20T01:25:30Z gs://{転送元バケット}/s3_sample/streaming_test8.csv 1045260 2023-05-20T01:28:54Z gs://{転送元バケット}/s3_sample/streaming_test9.csv $ aws s3 --profile cm_sspg-user ls s3://{転送先バケット}/s3_sample/ 2023-05-20 10:28:18 1045260 streaming_test1.csv 2023-05-20 10:28:22 1045260 streaming_test2.csv 2023-05-20 11:33:30 1045260 streaming_test3.csv 2023-05-20 11:33:29 1045260 streaming_test4.csv 2023-05-20 11:33:31 1045260 streaming_test5.csv 2023-05-20 11:33:29 1045260 streaming_test6.csv 2023-05-20 11:33:30 1045260 streaming_test7.csv 2023-05-20 11:33:30 1045260 streaming_test8.csv 2023-05-20 11:33:30 1045260 streaming_test9.csv
これでCloud Run Jobsを使っているのでインスタンスの管理などは必要なく、極力マネージドなサービを使ってGCSからS3への転送を行えました。
転送元と転送先は環境変数としてジョブ作成時に与えられるように作成したので新しい転送元と転送先がある場合はgcloud beta run jobs create
で都度新しいジョブを作成すれば簡単に新しいGCSからS3へを転送ジョブを作る事ができます。
今回は手動でジョブを実行しましたが、Cloud Run Jobsを使っているのでCloud Schedulerでスケジュール実行をおこなったり、Eventarcを使ってGCSへのオブジェクトアップロードをトリガーに転送ジョブを実行することもできます。
gsutil rsync
の注意点
今回作成したジョブで1点注意点があります。サイズが5GB以上のオブジェクトを扱う場合は以下のエラーが出ます。エラー内容の通り、5GB以上のオブジェクトをS3にアップロードする際にはマルチパートでアップロードする必要がありますが、gsutilでは対応していないため転送元のGCSには5GB未満のオブジェクトに分割してアップロードしておく必要があります。
CommandException: "gs://{ソースバケット}/s3_sample/streaming_test_5.csv" exceeds the maximum gsutil-supported size for an S3 upload. S3 objects greater than 5 GiB in size require multipart uploads, which gsutil does not support.
まとめ
GCSからS3へのオブジェクト転送を極力マネージドなサービスで実現する目的でCloud Run Jobsを使ってジョブを実装してみました。処理の中身もgsutilを使っているため実行するスクリプトも数行となり非常にシンプルになりました。
GCS to S3の仕組みをお手軽に実装できるので是非試してみてください。
最後まで読んで頂いてありがとうございました。